package com.gobang.websocket.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gobang.common.constant.BusinessErrorCode;
import com.gobang.util.RedisUtil;
import com.gobang.websocket.session.WebSocketSessionManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * WebSocket 消息推送服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WebSocketPushService {

    private final WebSocketSessionManager webSocketSessionManager;
    private final RedisUtil redisUtil;
    private final ObjectMapper objectMapper;

    /**
     * 点对点发送消息
     */
    public void sendToUser(Long userId, String type, Object data) {
        if (userId == null) {
            return;
        }
        WebSocketSession session = webSocketSessionManager.getSessionByUserId(userId);
        if (session == null || !session.isOpen()) {
            log.debug("用户当前没有有效的 WebSocket 会话: userId={}", userId);
            return;
        }
        sendToSession(session, type, data);
    }

    /**
     * 房间广播：根据 room:{roomId}:members 集合推送
     */
    public void broadcastToRoom(String roomId, String type, Object data) {
        if (!StringUtils.hasText(roomId)) {
            return;
        }
        String membersKey = "room:" + roomId + ":members";
        Set<String> members = redisUtil.sGet(membersKey);
        if (members == null || members.isEmpty()) {
            log.debug("房间暂无成员或不存在: roomId={}", roomId);
            return;
        }
        for (String userIdStr : members) {
            try {
                Long userId = Long.valueOf(userIdStr);
                sendToUser(userId, type, data);
            } catch (NumberFormatException e) {
                log.warn("房间成员 userId 非法: roomId={}, userIdStr={}", roomId, userIdStr);
            }
        }
    }

    /**
     * 全局广播（谨慎使用）
     */
    public void broadcastToAll(String type, Object data) {
        Map<Long, WebSocketSession> allSessions = webSocketSessionManager.getAllUserSessions();
        if (allSessions == null || allSessions.isEmpty()) {
            return;
        }
        for (Map.Entry<Long, WebSocketSession> entry : allSessions.entrySet()) {
            WebSocketSession session = entry.getValue();
            if (session != null && session.isOpen()) {
                sendToSession(session, type, data);
            }
        }
    }

    /**
     * 发送错误消息
     */
    public void sendError(WebSocketSession session, Integer code, String message) {
        if (session == null) {
            return;
        }
        Integer finalCode = code != null ? code : BusinessErrorCode.SYSTEM_ERROR.getCode();
        String finalMessage = StringUtils.hasText(message) ? message : BusinessErrorCode.SYSTEM_ERROR.getMessage();

        Map<String, Object> data = new HashMap<>();
        data.put("code", finalCode);
        data.put("message", finalMessage);

        sendToSession(session, "error", data);
    }

    /**
     * 发送错误消息（基于错误码枚举）
     */
    public void sendError(WebSocketSession session, BusinessErrorCode errorCode, String message) {
        Integer code = errorCode != null ? errorCode.getCode() : BusinessErrorCode.SYSTEM_ERROR.getCode();
        sendError(session, code, message != null ? message : (errorCode != null ? errorCode.getMessage() : null));
    }

    /**
     * 发送心跳响应
     */
    public void sendHeartbeat(WebSocketSession session) {
        Map<String, Object> data = new HashMap<>();
        data.put("serverTime", System.currentTimeMillis());
        sendToSession(session, "heartbeat", data);
    }

    /**
     * 底层发送封装
     */
    private void sendToSession(WebSocketSession session, String type, Object data) {
        if (session == null || !session.isOpen()) {
            return;
        }
        Map<String, Object> envelope = new HashMap<>();
        envelope.put("type", type);
        envelope.put("data", data);
        try {
            String json = objectMapper.writeValueAsString(envelope);
            session.sendMessage(new TextMessage(json));
        } catch (JsonProcessingException e) {
            log.error("序列化 WebSocket 消息失败: type={}, error={}", type, e.getMessage(), e);
        } catch (IOException e) {
            log.error("发送 WebSocket 消息失败: sessionId={}, type={}, error={}",
                    session.getId(), type, e.getMessage(), e);
            try {
                session.close(CloseStatus.SERVER_ERROR);
            } catch (IOException ex) {
                log.warn("关闭异常 WebSocket 会话失败: sessionId={}", session.getId(), ex);
            }
        }
    }
}
